﻿''' <summary>
''' Parameter
''' </summary>
''' <remarks></remarks>
Public Class BaseParameter(Of InputType, OutputType)

#Region "Constructors"


    ''' <summary>
    ''' New Parameter
    ''' </summary>
    ''' <param name="value "></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal value As InputType)
        Me.SetValue(value)
    End Sub

    ''' <summary>
    ''' New Parameter with a value and ruleset
    ''' </summary>
    ''' <param name="value "></param>
    ''' <remarks></remarks>
    Public Sub New(ByVal value As InputType, ByVal ruleSet As RuleSet(Of InputType))
        Me.New(value)
        Me.RuleSet = ruleSet
    End Sub

    ''' <summary>
    ''' New Parameter
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub New()
    End Sub

#End Region


    Private _UnsecureValue As InputType

    ''' <summary>
    ''' Set value to confirm
    ''' </summary>
    ''' <param name="value"></param>
    ''' <remarks></remarks>
    Public Sub SetValue(ByVal value As InputType)
        Invalidate()

        'Set Local unsecure value
        _UnsecureValue = value

        'Check Rules
        ValidateRules()
    End Sub

    ''' <summary>
    ''' Invalidate current checks
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub Invalidate()
        'Invalidate it first
        _IsValid = False
        _Value = Nothing
    End Sub

    Private _RuleSet As RuleSet(Of InputType)
    ''' <summary>
    ''' Rule Set for this parameter
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property RuleSet() As RuleSet(Of InputType)
        Get
            Return _RuleSet
        End Get
        Set(ByVal value As RuleSet(Of InputType))
            _RuleSet = value

            Invalidate()
            ValidateRules()
        End Set
    End Property


    Private _IsValid As Boolean

    ''' <summary>
    ''' Is this parameter valid
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property IsValid() As Boolean
        Get
            Return _IsValid
        End Get
    End Property


    ''' <summary>
    ''' Validate Rules and Set IsValid
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub ValidateRules()
        'Check Rules
        If RuleSet Is Nothing Then Exit Sub

        'Set Validity
        _IsValid = Me.RuleSet.IsParameterValid(_UnsecureValue)
    End Sub


    ''' <summary>
    ''' Process value
    ''' </summary>
    ''' <remarks>
    ''' Check against rules and sets the value if it's possible
    ''' </remarks>
    Private Sub ValidateValue()
        Try
            ValidateRules()
            If Not Me.IsValid Then Exit Sub

            'Convert it
            Dim LocalUnsecure As InputType = _UnsecureValue
            LocalUnsecure = Me.RuleSet.Modify(LocalUnsecure)

            _Value = GetAsOutputType(LocalUnsecure)

        Catch ex As Exception
            _IsValid = False

        End Try
    End Sub

    ''' <summary>
    ''' Get it as Output Type
    ''' </summary>
    ''' <param name="input"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GetAsOutputType(ByVal input As InputType) As OutputType
        'TODO: We might need to fix here depending on different types
        'For now this should provide most flexible way to us
        'If developers use some weird types, Due auto conversion we might have a problem from security point of view.
        Return CType(Convert.ChangeType(input, GetType(OutputType), Globalization.CultureInfo.InvariantCulture), OutputType)
    End Function


    Private _Value As OutputType

    ''' <summary>
    ''' Gets the value.
    ''' </summary>
    ''' <value>The value.</value>
    Public ReadOnly Property Value() As OutputType
        Get
            ValidateValue()
            Return _Value
        End Get
    End Property

    ''' <summary>
    ''' Get Value based on a spesific RuleSet
    ''' </summary>
    ''' <param name="ruleset"></param>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Value(ByVal ruleSet As RuleSet(Of InputType)) As OutputType
        Get
            Me.RuleSet = ruleSet
            Return Me.Value
        End Get
    End Property

    ''' <summary>
    ''' Escapes parameter value based on supplier Modifier.
    ''' </summary>
    ''' <param name="modifier">The modifier.</param>
    ''' <param name="ForceToEscape">if set to <c>true</c> [force to escape].</param>
    ''' <returns></returns>
    ''' <remarks>It uses ToString() to convert type to string. If you've got some custom Object you should override ToString() get this work as expected.</remarks>
    Public Function EscapeTo(ByVal modifier As IModifier(Of String), ByVal ForceToEscape As Boolean) As String
        If Value Is Nothing Then Return Nothing

        'Check if there are already output modifiers, It's not a good idea to escape it after those modifiers
        If Not ForceToEscape Then

            If Me.RuleSet IsNot Nothing AndAlso Me.RuleSet.Modifiers.Count > 0 Then
                Throw New System.Security.SecurityException("There are alredy some modifiers in the used RuleSet. Encoding an output twice with same or different encoding can cause some vulnerabilities. To force encoding you can use ForceToEscape parameter.")

                Return Nothing
            End If

        End If

        Return modifier.Convert(Me.Value.ToString())
    End Function

    ''' <summary>
    ''' Escapes to.
    ''' </summary>
    ''' <param name="modifier">The modifier.</param>
    ''' <returns></returns>
    Public Function EscapeTo(ByVal modifier As IModifier(Of String)) As String
        Return EscapeTo(modifier, False)
    End Function


    ''' <summary>
    ''' Escapes for Html Output.
    ''' </summary>
    ''' <returns></returns>
    Public Function EscapeToHtml() As String
        Return EscapeTo(New Modifiers.HTML, False)
    End Function

End Class
